;; Test file for essential procedures: procedure?, apply, and map
;; This file demonstrates the functionality of the newly implemented procedures

(define (test-description description)
  (begin
    (display "Testing: ")
    (display description)
    (newline)))

;; Test procedure? predicate
(test-description "procedure? predicate")

;; Testing built-in procedures
(procedure? +)              ;; => #t
(procedure? cons)           ;; => #t
(procedure? car)            ;; => #t
(procedure? apply)          ;; => #t
(procedure? map)            ;; => #t

;; Testing non-procedures
(procedure? 42)             ;; => #f
(procedure? "string")       ;; => #f
(procedure? '(1 2 3))       ;; => #f
(procedure? #t)             ;; => #f

;; Testing user-defined procedures
(define square (lambda (x) (* x x)))
(procedure? square)         ;; => #t

(define (factorial n)
  (if (= n 0)
      1
      (* n (factorial (- n 1)))))
(procedure? factorial)      ;; => #t

;; Test apply procedure
(test-description "apply procedure")

;; Apply with arithmetic operations
(apply + '(1 2 3 4 5))      ;; => 15
(apply * '(2 3 4))          ;; => 24
(apply - '(20 5))           ;; => 15
(apply / '(24 6))           ;; => 4

;; Apply with list operations
(apply cons '(1 (2 3 4)))   ;; => (1 2 3 4)
(apply list '(a b c d))     ;; => (a b c d)
(apply car '((hello world))) ;; => hello
(apply cdr '((a b c d)))    ;; => (b c d)

;; Apply with user-defined functions
(apply square '(7))         ;; => 49
(apply factorial '(5))      ;; => 120

;; Apply with multiple argument functions
(define add3 (lambda (x y z) (+ x y z)))
(apply add3 '(1 2 3))       ;; => 6

;; Apply with empty lists (for operations that support it)
(apply + '())               ;; => 0
(apply * '())               ;; => 1
(apply list '())            ;; => ()

;; Test map procedure
(test-description "map procedure")

;; Map with single list
(map square '(1 2 3 4 5))           ;; => (1 4 9 16 25)
(map abs '(-1 2 -3 4 -5))          ;; => (1 2 3 4 5)
(map factorial '(0 1 2 3 4))       ;; => (1 1 2 6 24)

;; Map with multiple lists
(map + '(1 2 3) '(4 5 6))          ;; => (5 7 9)
(map * '(1 2 3) '(4 5 6))          ;; => (4 10 18)
(map - '(10 20 30) '(1 2 3))       ;; => (9 18 27)
(map cons '(a b c) '((1) (2) (3))) ;; => ((a 1) (b 2) (c 3))

;; Map with lists of different lengths (uses shortest)
(map + '(1 2 3 4 5) '(10 20))      ;; => (11 22)

;; Map with three lists
(map add3 '(1 2 3) '(4 5 6) '(7 8 9)) ;; => (12 15 18)

;; Map with empty lists
(map + '() '())                     ;; => ()
(map square '())                    ;; => ()

;; Complex example: Using map and apply together
(test-description "Combining map and apply")

;; Define a function that finds the maximum of a list
(define (max-of-list lst)
  (if (null? lst)
      0
      (apply max lst)))

;; Use map to find max of multiple lists
(define lists '((1 5 3) (2 8 4) (7 1 9)))
(map max-of-list lists)             ;; => (5 8 9)

;; Advanced example: Function composition using apply and map
(define (compose f g)
  (lambda (x) (f (g x))))

(define inc (lambda (x) (+ x 1)))
(define double (lambda (x) (* x 2)))
(define inc-then-double (compose double inc))

(map inc-then-double '(1 2 3 4))    ;; => (4 6 8 10)

;; Example: Using map to transform nested lists
(define nested-lists '((1 2) (3 4) (5 6)))
(map (lambda (lst) (apply + lst)) nested-lists) ;; => (3 7 11)

;; Example: Functional programming style - filter using map and boolean logic
;; Since we don't have filter yet, we can simulate it using map and apply
(define (keep-if pred lst)
  (define (helper item)
    (if (pred item) (list item) '()))
  (apply append (map helper lst)))

(define is-even (lambda (x) (= 0 (remainder x 2))))
(keep-if is-even '(1 2 3 4 5 6 7 8)) ;; => (2 4 6 8)

(test-description "All tests completed!")